#include "requestdispatcher.h"
using namespace FsxWebService;

//
// Constructor
//
RequestDispatcher::RequestDispatcher(const KmlSettings &kmlSettings, const QString &navigraphDir, QObject* parent) :
    WscRequestHandler(-1, RequestDispatcher::getDocrootUrl(), "/", parent),
    _wsDummyPerson(nullptr), _wsAircraftsJson(nullptr), _wsNavigraph(nullptr),
    _staticFileController(nullptr), _listener(nullptr), _shutdown(false)
{
    RequestDispatcher::_settingsFile = RequestDispatcher::getIniFilename();
    QSettings* settingsListener=new QSettings(RequestDispatcher::_settingsFile ,QSettings::IniFormat, parent);
    settingsListener->beginGroup("listener");
    RequestDispatcher::setPort(settingsListener->value("port").toInt());
    QSettings* settingsDocRoot=new QSettings(RequestDispatcher::_settingsFile ,QSettings::IniFormat, parent);
    settingsDocRoot->beginGroup("docroot");

    RequestDispatcher::_wsDummyPerson = new WscDummyPerson(getPort(), getServerUrl(), "/dummyperson",  parent);
    RequestDispatcher::_wsAircraftsJson = new WscAircraftsJson(getPort(), getServerUrl(), "/aircraftsJson", parent);
    RequestDispatcher::_wsAircraftKmlWaypoints = new WscAircraftKmlWaypoints(kmlSettings, getPort(), getServerUrl(), "/aircraftWaypointsKml", parent);
    RequestDispatcher::_wsAircraftsHtml = new WscAircraftsHtmlOverview(getPort(), getServerUrl(), "/aircraftsHtml", parent);
    RequestDispatcher::_staticFileController = new StaticFileController(settingsDocRoot, parent);
    if (navigraphDir.isEmpty() || !Utils::isValidFmcDataDirectory(navigraphDir)) {
        // log some info?
    } else {
        QSettings* settingsWsNaviGraph = new QSettings(settingsDocRoot);
        // avoid writing to file
        settingsWsNaviGraph->setPath(QSettings::InvalidFormat, QSettings::UserScope, "");
        settingsWsNaviGraph->beginGroup("docroot");
        settingsWsNaviGraph->setValue("path", navigraphDir);
        RequestDispatcher::_wsNavigraph = new StaticFileController(settingsWsNaviGraph, parent);
        delete settingsWsNaviGraph;
    }
    RequestDispatcher::_listener=new HttpListener(settingsListener, this, parent);
}

//
// Destructor
//
RequestDispatcher::~RequestDispatcher() {
    RequestDispatcher::terminate();
}

//
// Terminate this dispatcher, the corresponding listener and shut down
//
void RequestDispatcher::terminate() {

    if (_shutdown || (!RequestDispatcher::_listener)) return; // already done
    _shutdown = true;

    // Originally suffering from Memory Leak
    // Deleting the listener / handlers resulted in assert error
    // For some reasons this seems to be the cure: RequestDispatcher::_listener->setMaxPendingConnections(0)

    if (RequestDispatcher::_listener) {
        // graceful termination
        RequestDispatcher::_listener->setMaxPendingConnections(0);
        RequestDispatcher::_listener->disconnect();
        RequestDispatcher::_listener->close();
        QTcpSocket* socket;
        while((socket=RequestDispatcher::_listener->nextPendingConnection())) {
            socket->close();
        }
        delete RequestDispatcher::_listener;
        RequestDispatcher::_listener = nullptr;
    }

    delete RequestDispatcher::_wsDummyPerson;
    RequestDispatcher::_wsDummyPerson = nullptr;
    delete RequestDispatcher::_wsAircraftsJson;
    RequestDispatcher::_wsAircraftsJson = nullptr;
    delete RequestDispatcher::_wsAircraftsHtml;
    RequestDispatcher::_wsAircraftsHtml = nullptr;
    delete RequestDispatcher::_wsAircraftKmlWaypoints;
    RequestDispatcher::_wsAircraftKmlWaypoints = nullptr;
    delete RequestDispatcher::_wsNavigraph;
    RequestDispatcher::_wsNavigraph = nullptr;
    delete RequestDispatcher::_staticFileController;
    RequestDispatcher::_staticFileController = nullptr;

}

//
// Full URL of the docroot
//
const QString RequestDispatcher::getDocrootUrl()
{
    QSettings settings(RequestDispatcher::getIniFilename(),QSettings::IniFormat);
    bool success = false;
    settings.beginGroup("listener");
    int port = settings.value("port").toInt(&success);
    if (!success) port = 80;
    settings.endGroup();
    QString url = "http://localhost";
    if (port != 80) url.append(":" + QString::number(port));
    url.append("/");
    return url;
}

//
// Corresponding ini file
//
const QString RequestDispatcher::getIniFilename() {
    QString settingsFile = QApplication::applicationDirPath() + "/config/WebService.ini";
    // check the file existence
    if (!QFile::exists(settingsFile)) {
        QString m = "Settings file " + settingsFile + " not found";
        QByteArray mByteArray = m.toUtf8();
        const char* mEx = mByteArray.constData();
        throw std::exception(mEx);
    }
    return settingsFile;
}

//
// Dispatch to sub services
//
void RequestDispatcher::service(HttpRequest& request, HttpResponse& response) {

    // shutdown in progress?
    if (_shutdown) {
        response.setStatus(500,"Dispatcher shutting down");
        response.write("500 dispatcher going down",true);
        return;
    }

    // Logging
    QByteArray path = request.getPath();
    QString pathS = QString(path);
    qDebug("RequestMapper: path=%s",path.data());

    // Globally add a cross domain header for XmlHttpRequest
    // http://www.leggetter.co.uk/2010/03/12/making-cross-domain-javascript-requests-using-xmlhttprequest-or-xdomainrequest.html
    response.setHeader("Access-Control-Allow-Origin", "*");

    // dispatch
    if (RequestDispatcher::_wsDummyPerson && RequestDispatcher::_wsDummyPerson->isMatchingPath(path)) {
        // just for testing
        RequestDispatcher::_wsDummyPerson->service(request, response);
    } else if (RequestDispatcher::_wsAircraftsJson && RequestDispatcher::_wsAircraftsJson->isMatchingPath(path)) {
        // JSON objects
        RequestDispatcher::_wsAircraftsJson->service(request, response);
    } else if (RequestDispatcher::_wsAircraftsHtml && RequestDispatcher::_wsAircraftsHtml->isMatchingPath(path)) {
        // aircrafts as HTML table
        RequestDispatcher::_wsAircraftsHtml->service(request, response);
    } else if (RequestDispatcher::_wsAircraftKmlWaypoints && RequestDispatcher::_wsAircraftKmlWaypoints->isMatchingPath(path)) {
        // aircraft waypoints as KML
        RequestDispatcher::_wsAircraftKmlWaypoints->service(request, response);
    } else if (RequestDispatcher::_wsNavigraph && RequestDispatcher::isNavigraphPath(pathS)) {
        // allows to read Navigraph file
        RequestDispatcher::_wsNavigraph->service(request, response);
    }
    else {
        // static files in docroot
        if (RequestDispatcher::_staticFileController) RequestDispatcher::_staticFileController->service(request, response);
    }
}

//
// Is Navigraph request path
//
bool RequestDispatcher::isNavigraphPath(const QString &path) {
    if (path.isEmpty()) return false;
    return (path.contains("SIDSTARS", Qt::CaseInsensitive) ||
            path.contains("NavData", Qt::CaseInsensitive));
}
